// Copyright (c) MatrixFramework. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System.Collections.Concurrent;
using System.Net;
using System.Net.Http;
using System.Threading;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Resilience;
using Polly;
using Matrix.ServiceCommunication.Options;

namespace Matrix.ServiceCommunication.Resilience;

/// <summary>
/// 动态容错策略提供者
/// </summary>
public class DynamicResilienceStrategyProvider : IResilienceStrategyProvider, IDisposable
{
    private readonly IOptionsMonitor<ResilienceOptions> _options;
    private readonly ILogger<DynamicResilienceStrategyProvider> _logger;
    private readonly ConcurrentDictionary<string, ResiliencePipeline<HttpResponseMessage>> _strategies;
    private readonly ResiliencePipeline<HttpResponseMessage> _defaultStrategy;
    private readonly object _defaultStrategyLock = new object();
    private volatile bool _defaultStrategyNeedsUpdate = true;

    public DynamicResilienceStrategyProvider(
        IOptionsMonitor<ResilienceOptions> options,
        ILogger<DynamicResilienceStrategyProvider> logger)
    {
        _options = options ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        _strategies = new ConcurrentDictionary<string, ResiliencePipeline<HttpResponseMessage>>();

        // 订阅配置变更
        _options.OnChange(_ =>
        {
            _logger.LogInformation("容错配置已更新，清除所有策略缓存");
            _defaultStrategyNeedsUpdate = true;
            _strategies.Clear();
        });

        _logger.LogInformation("动态容错策略提供者已初始化");
    }

    public ResiliencePipeline<HttpResponseMessage> GetStrategy(string serviceName)
    {
        if (string.IsNullOrWhiteSpace(serviceName))
        {
            return GetDefaultStrategy();
        }

        return _strategies.GetOrAdd(serviceName, BuildStrategy);
    }

    public ResiliencePipeline<HttpResponseMessage> GetDefaultStrategy()
    {
        if (_defaultStrategyNeedsUpdate)
        {
            lock (_defaultStrategyLock)
            {
                if (_defaultStrategyNeedsUpdate)
                {
                    var options = _options.CurrentValue;
                    var newStrategy = BuildDefaultStrategy(options);
                    _strategies["default"] = newStrategy;
                    _defaultStrategyNeedsUpdate = false;

                    _logger.LogInformation("默认容错策略已更新");
                }
            }
        }

        return _strategies.TryGetValue("default", out var strategy)
            ? strategy
            : BuildDefaultStrategy(_options.CurrentValue);
    }

    public void ClearStrategyCache(string serviceName)
    {
        if (string.IsNullOrWhiteSpace(serviceName))
        {
            return;
        }

        _strategies.TryRemove(serviceName, out _);
        _logger.LogDebug("已清除服务 {ServiceName} 的容错策略缓存", serviceName);
    }

    public void ClearAllStrategyCache()
    {
        _strategies.Clear();
        _defaultStrategyNeedsUpdate = true;
        _logger.LogInformation("已清除所有容错策略缓存");
    }

    private ResiliencePipeline<HttpResponseMessage> BuildStrategy(string serviceName)
    {
        try
        {
            var options = _options.CurrentValue;
            var strategy = BuildResiliencePipeline(options);

            _logger.LogDebug("已为服务 {ServiceName} 构建容错策略", serviceName);
            return strategy;
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "为服务 {ServiceName} 构建容错策略失败，使用默认策略", serviceName);
            return GetDefaultStrategy();
        }
    }

    private ResiliencePipeline<HttpResponseMessage> BuildDefaultStrategy(ResilienceOptions options)
    {
        return BuildResiliencePipeline(options);
    }

    private ResiliencePipeline<HttpResponseMessage> BuildResiliencePipeline(ResilienceOptions options)
    {
        var builder = new ResiliencePipelineBuilder<HttpResponseMessage>();

        // 1. 添加超时策略（最外层）
        builder.AddTimeout(TimeSpan.FromSeconds(options.Timeout.TimeoutSeconds));

        // 2. 添加重试策略
        var retryOptions = options.Retry;
        builder.AddRetry(new RetryStrategyOptions<HttpResponseMessage>
        {
            MaxRetryAttempts = retryOptions.MaxAttempts,
            Delay = TimeSpan.FromMilliseconds(retryOptions.DelayMilliseconds),
            BackoffType = GetPollyBackoffType(retryOptions.BackoffType),
            ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                .Handle<HttpRequestException>()
                .Handle<TimeoutException>()
                .HandleResult(response =>
                    response.StatusCode >= HttpStatusCode.InternalServerError ||
                    response.StatusCode == HttpStatusCode.RequestTimeout ||
                    response.StatusCode == HttpStatusCode.BadGateway ||
                    response.StatusCode == HttpStatusCode.ServiceUnavailable ||
                    response.StatusCode == HttpStatusCode.GatewayTimeout),
            OnRetry = args =>
            {
                var attempt = args.AttemptNumber;
                var delay = args.RetryDelay;
                var exception = args.Outcome.Exception;
                var statusCode = args.Outcome.Result?.StatusCode;

                _logger.LogWarning(
                    exception,
                    "HTTP请求重试 #{Attempt}, 延迟 {Delay}ms, 状态码: {StatusCode}",
                    attempt, delay.TotalMilliseconds, statusCode);

                return ValueTask.CompletedTask;
            }
        });

        // 3. 添加断路器策略
        var circuitBreakerOptions = options.CircuitBreaker;
        builder.AddCircuitBreaker(new CircuitBreakerStrategyOptions<HttpResponseMessage>
        {
            FailureRatio = circuitBreakerOptions.FailureRatio,
            SamplingDuration = TimeSpan.FromSeconds(circuitBreakerOptions.SamplingDurationSeconds),
            MinimumThroughput = circuitBreakerOptions.MinimumThroughput,
            BreakDuration = TimeSpan.FromSeconds(circuitBreakerOptions.BreakDurationSeconds),
            ShouldHandle = new PredicateBuilder<HttpResponseMessage>()
                .Handle<HttpRequestException>()
                .Handle<TimeoutException>()
                .HandleResult(response =>
                    response.StatusCode >= HttpStatusCode.InternalServerError ||
                    response.StatusCode == HttpStatusCode.RequestTimeout),
            OnOpened = args =>
            {
                _logger.LogWarning(
                    "断路器已打开，失败率: {FailureRatio:P2}, 持续时间: {Duration}s",
                    circuitBreakerOptions.FailureRatio,
                    circuitBreakerOptions.BreakDurationSeconds);

                return ValueTask.CompletedTask;
            },
            OnClosed = args =>
            {
                _logger.LogInformation("断路器已关闭");
                return ValueTask.CompletedTask;
            },
            OnHalfOpened = args =>
            {
                _logger.LogInformation("断路器已半开");
                return ValueTask.CompletedTask;
            }
        });

        return builder.Build();
    }

    private static DelayBackoffType GetPollyBackoffType(BackoffType backoffType)
    {
        return backoffType switch
        {
            BackoffType.Constant => DelayBackoffType.Constant,
            BackoffType.Linear => DelayBackoffType.Linear,
            BackoffType.Exponential => DelayBackoffType.Exponential,
            _ => DelayBackoffType.Exponential
        };
    }

    public void Dispose()
    {
        _strategies.Clear();
        _logger.LogDebug("动态容错策略提供者已释放");
    }
}